# Copyright (c) HySoP 2011-2024
#
# This file is part of HySoP software.
# See "https://particle_methods.gricad-pages.univ-grenoble-alpes.fr/hysop-doc/"
# for further info.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import pyopencl as cl
from contextlib import contextmanager
from hysop.tools.htypes import check_instance
from hysop.backend.device.codegen.base.codegen import CodeGenerator
from hysop.backend.device.codegen.base.opencl_codegen import OpenClCodeGenerator
from hysop.backend.device.codegen.base.utils import ArgDict
from hysop.backend.device.opencl.opencl_types import TypeGen, OpenClTypeGen
from hysop.backend.device.codegen.base.variables import (
CodegenVariable,
CodegenVectorClBuiltin,
CodegenVectorClBuiltinFunc,
)
[docs]
class FunctionBase:
def __init__(
self,
fname,
output,
fargs,
known_args,
typegen,
symbolic_mode=None,
inline=False,
**kargs,
):
super().__init__(typegen=typegen, **kargs)
known_args = ArgDict() if (known_args is None) else known_args
output = "void" if (output is None) else output
check_instance(fargs, ArgDict)
fargs.release()
for varname, varval in known_args.items():
if varname in fargs.keys():
if isinstance(varval, CodegenVariable):
if not varval.known():
pass
else:
varval.const = True
if symbolic_mode:
varval.force_symbolic(symbolic_mode)
fargs[varname] = varval
else:
try:
fargs[varname].set_value(varval)
except ValueError:
var = fargs[varname]
print()
print(
f"FATAL ERROR: Failed to set value of known variable {varname}."
)
print(f" *variable type was {type(var).__name__}")
if hasattr(var, "dim"):
print(f" *variable dim was {var.dim}")
print(f" *value was of type {type(varval)}")
print(f" *value was {varval}")
print()
raise
fargs[varname].const = True
if symbolic_mode:
fargs[varname].force_symbolic(symbolic_mode)
fargs.lock()
self.fname = fname
self.output = output
self.inline = inline
self.args = fargs
self.known_args = known_args
def __call__(self, *var, **kvars):
return f"{self.fname}({self.match_args(*var,**kvars)})"
def _return(self, what):
code = f"return {what};"
self.append(code)
[docs]
def match_args(self, *vars, **kvars):
args = self.args
arg_order = args.arg_order
sargs = []
for argname in arg_order:
arg = args[argname]
if not arg.known():
if len(vars) > 0:
var = vars.pop(0)
elif argname in kvars:
var = kvars[argname]
else:
svars = "".join([f"\n\t{var}" for var in sorted(vars)])
skvars = "".join(
[
f"\n\t{kname} -> {kvars[kname]}"
for kname in sorted(kvars.keys())
]
)
if len(svars) != 0:
svars += "\n "
if len(skvars) != 0:
skvars += "\n "
msg = """Could not match function argument '{}' of type '{}' while calling function '{}' from:
vars = [{}]
kvars = {}{}{}
""".format(
argname, arg.ctype, self.fname, svars, "{", skvars, "}"
)
raise RuntimeError(msg)
if isinstance(var, str):
sargs.append(var)
elif isinstance(var, arg.__class__) or (
arg.__class__ == CodegenVectorClBuiltin
and arg.dim == 1
and isinstance(var, (CodegenVariable, str))
):
sargs.append(var.name)
else:
msg = "Matched variable '{}' with argument '{}' but there was a type mismatch('{}' is not an instance of '{}')!".format(
getattr(var, "name", "unknown"),
arg.name,
var.__class__,
arg.__class__,
)
raise RuntimeError(msg)
return ", ".join(sargs)
[docs]
class FunctionCodeGenerator(FunctionBase, CodeGenerator):
def __init__(
self,
basename,
typegen,
output,
args=None,
known_args=None,
inline=False,
ext=".tmp",
):
check_instance(typegen, TypeGen)
name = basename
fname = basename
if args is not None:
check_instance(args, ArgDict)
fname += args.function_name_suffix(output, known_args)
name += args.codegen_name_suffix(output, known_args)
super().__init__(
name=name,
typegen=typegen,
fname=fname,
output=output,
inline=inline,
ext=ext,
fargs=args,
known_args=known_args,
)
self.inject_vars(args)
@contextmanager
def _function_(self):
name = self.fname
output = self.output
fargs, fargs_impl, cargs = self.args.build_args()
with super()._function_(
name=name,
output=output,
args=fargs,
args_impl=fargs_impl,
inline=self.inline,
) as f:
for c in cargs:
self.append(c)
yield f
[docs]
class OpenClFunctionCodeGenerator(FunctionBase, OpenClCodeGenerator):
def __init__(
self,
basename,
typegen,
output,
args=None,
known_args=None,
inline=False,
ext=".cl",
):
check_instance(typegen, OpenClTypeGen)
name = basename
fname = basename
if args is not None:
check_instance(args, ArgDict)
fname += args.function_name_suffix(output, known_args)
name += args.codegen_name_suffix(output, known_args)
super().__init__(
name=name,
typegen=typegen,
fname=fname,
output=output,
inline=inline,
ext=ext,
fargs=args,
known_args=known_args,
)
self.inject_vars(args)
@contextmanager
def _function_(self):
name = self.fname
output = self.output
fargs, fargs_impl, cargs = self.args.build_args()
with super()._function_(
name=name,
output=output,
args=fargs,
args_impl=fargs_impl,
inline=self.inline,
) as f:
for c in cargs:
c.declare(self)
yield f